package org.theSeed.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.redisson.api.RBatch;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RMapAsync;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.theSeed.base.exp.BuzExp;
import org.theSeed.base.pojo.constant.SysConstant;
import org.theSeed.base.pojo.dto.CommonKeyValue;
import org.theSeed.base.pojo.ro.PageRo;
import org.theSeed.base.pojo.sysEnum.SysCode;
import org.theSeed.mapper.SysResourceMapper;
import org.theSeed.mapper.SysRoleMapper;
import org.theSeed.mapper.SysRoleResourceRelMapper;
import org.theSeed.pojo.dto.SysResourceAndRoleCode;
import org.theSeed.pojo.po.SysResource;
import org.theSeed.pojo.po.SysRole;
import org.theSeed.pojo.po.SysRoleResourceRel;
import org.theSeed.pojo.ro.EditRoleRo;
import org.theSeed.rbacBaseCommon.pojo.constant.RbacConstant;
import org.theSeed.rbacBaseCommon.pojo.dto.CacheRoleInfoDto;
import org.theSeed.rbacBaseCommon.pojo.sysEnum.ResourceTypeEnum;
import org.theSeed.redisIntegration.utils.RedisUtil;
import org.theSeed.service.SysRoleService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class SysRoleServiceImpl implements SysRoleService {
    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysResourceMapper sysResourceMapper;

    @Autowired
    private SysRoleResourceRelMapper sysRoleResourceRelMapper;

    @Override
    public Page<List<SysRole>> listRole(PageRo pageRo) {
        return PageHelper.startPage(pageRo.getPageNum(), pageRo.getPageSize())
                .doSelectPage(() -> {
                    sysRoleMapper.selectList(Wrappers.lambdaQuery());
                });
    }

    @Override
    public SysRole getRoleDetail(String id) {
        return sysRoleMapper.selectById(id);
    }

    @Override
    @Transactional
    public void editRole(EditRoleRo editRoleRo) {
        //角色更新
        SysRole sysRole = BeanUtil.copyProperties(editRoleRo, SysRole.class);
        sysRole.setEnableFlag(SysConstant.Y);
        sysRole.setRoleCode(IdUtil.fastUUID().toString());
        if (ObjectUtil.isNotEmpty(editRoleRo.getId())) {
            sysRoleMapper.updateById(sysRole);
            sysRole = sysRoleMapper.selectById(sysRole.getId());
        } else {
            sysRoleMapper.insert(sysRole);
        }

        //缓存刷新
        if (ObjectUtil.isNotNull(sysRole)) {
            CacheRoleInfoDto cacheRoleInfoDto = new CacheRoleInfoDto();
            cacheRoleInfoDto.setRoleCode(sysRole.getRoleCode());
            cacheRoleInfoDto.setRoleName(sysRole.getRoleName());
            cacheRoleInfoDto.setEnableFlag(sysRole.getEnableFlag());
            RedisUtil.getRedissonClient().getBucket(RbacConstant.SEED_ROLE_INFO_PREFIX + sysRole.getRoleCode()).set(cacheRoleInfoDto);
        }
    }

    @Override
    @Transactional
    public void enableRole(CommonKeyValue<String, String> enableRo) {
        SysRole tmpRole = sysRoleMapper.selectById(enableRo.getKey());
        if (ObjectUtil.isNotNull(tmpRole)) {
            SysRole sysRole = new SysRole();
            sysRole.setId(enableRo.getKey());
            sysRole.setEnableFlag(enableRo.getValue());
            sysRoleMapper.updateById(sysRole);

            //缓存刷新
            if (ObjectUtil.isNotNull(sysRole)) {
                RBucket<CacheRoleInfoDto> bucket = RedisUtil.getRedissonClient().getBucket(RbacConstant.SEED_ROLE_INFO_PREFIX + tmpRole.getRoleCode());
                CacheRoleInfoDto cacheRoleInfoDto = bucket.get();
                cacheRoleInfoDto.setEnableFlag(sysRole.getEnableFlag());
                RedisUtil.getRedissonClient().getBucket(RbacConstant.SEED_ROLE_INFO_PREFIX + tmpRole.getRoleCode()).set(cacheRoleInfoDto);
            }
        }
    }

    @Override
    @Transactional
    public void deleteRole(String id) {
        SysRole tmpRole = sysRoleMapper.selectById(id);
        if (ObjectUtil.isNotNull(tmpRole)) {
            sysRoleMapper.deleteById(id);
            //缓存刷新
            RedisUtil.getRedissonClient().getKeys().delete(RbacConstant.SEED_ROLE_INFO_PREFIX + tmpRole.getRoleCode());
        }
    }

    @Override
    @Transactional
    public void authResource(String roleId, Set<String> resourceIdList) {
        RLock lock = RedisUtil.getRedissonClient().getLock(RbacConstant.PERMISSION_UPDATE_PREFIX + SysConstant.SEED);
        if (lock.tryLock()) {
            try {
                SysRole tmpRole = sysRoleMapper.selectById(roleId);
                if (ObjectUtil.isNotNull(tmpRole)) {
                    //删除已有权限
                    sysRoleResourceRelMapper.delete(Wrappers.lambdaQuery(SysRoleResourceRel.class).eq(SysRoleResourceRel::getRoleCode, tmpRole.getRoleCode()));

                    //添加权限
                    if (CollectionUtil.isNotEmpty(resourceIdList)) {
                        List<SysResource> sysResources = sysResourceMapper.selectList(Wrappers.lambdaQuery(SysResource.class).in(SysResource::getId, resourceIdList));
                        if (CollectionUtil.isNotEmpty(sysResources)) {
                            List<SysRoleResourceRel> tmpList = new ArrayList<>();
                            for (SysResource resource : sysResources) {
                                SysRoleResourceRel sysRoleResourceRel = new SysRoleResourceRel();
                                sysRoleResourceRel.setRoleCode(tmpRole.getRoleCode());
                                sysRoleResourceRel.setResourceCode(resource.getResourceCode());
                                tmpList.add(sysRoleResourceRel);
                            }
                            sysRoleResourceRelMapper.insertBatchSomeColumn(tmpList);
                        }
                    }

                    //缓存刷新
                    String roleCode = tmpRole.getRoleCode();
                    String roleResourcePermissionKey = RbacConstant.RESOURCE_PERMISSION_KEY_PREFIX + roleCode;
                    String roleApiPermissionKey = RbacConstant.API_PERMISSION_KEY_PREFIX + roleCode;
                    RBatch batch = RedisUtil.getRedissonClient().createBatch();

                    List<SysResourceAndRoleCode> sysRoleResourceRels = sysResourceMapper.findResourceAndRoleCodeByRoleId(roleId);
                    if (CollectionUtil.isNotEmpty(sysRoleResourceRels)) {
                        //刷新key
                        List<SysResourceAndRoleCode> usefulResource = sysRoleResourceRels.stream()
                                .filter(ele -> ObjectUtil.isNotEmpty(ele.getId()))
                                .collect(Collectors.toList());
                        if (CollectionUtil.isNotEmpty(usefulResource)) {
                            HashMap<String, Object> menuMap = new HashMap<>();
                            HashMap<String, Object> permissionMap = new HashMap<>();
                            usefulResource
                                    .stream()
                                    .forEach(son -> {
                                        if (ObjectUtil.isNotEmpty(son.getResourceCode())) {
                                            if (ResourceTypeEnum.isMenu(son.getResourceType())) {
                                                menuMap.put(son.getResourceCode(), son.getResourceCode());
                                            } else {
                                                permissionMap.put(son.getResourceCode(), son.getResourceCode());
                                            }
                                        }
                                    });
                            batch.getKeys().deleteAsync(roleResourcePermissionKey, roleApiPermissionKey);
                            batch.getMap(roleResourcePermissionKey).putAllAsync(menuMap);
                            batch.getMap(roleApiPermissionKey).putAllAsync(permissionMap);
                        }
                        batch.execute();
                    } else {
                        //删除key
                        batch.getKeys().deleteAsync(roleResourcePermissionKey, roleApiPermissionKey);
                    }
                }
            } finally {
                lock.unlock();
            }
        }else{
            throw new BuzExp(SysCode.MANY_REQUEST);
        }
    }
}
